#include "data.h"
#include <stdio.h>
#include <stdlib.h>

#define PI 3.14159265358979323846

// hamming window function
void hamming(unsigned n, double *d)
{
    if (n == 1) {
        d[0] = 1.0;
        return;
    }
    unsigned m = n - 1;
    for (unsigned i = 0; i < n; i++) {
        d[i] = 0.54 - 0.46 * cos((2.0 * PI * i) / m);
    }
}

double sinc(const double x)
{
    if(x == 0) return 1;
    return sin(PI * x) / (PI * x);
}

void gen_coeffs(int ntap,int nfft,double *coeffs)
{
    if(ntap <= 0 || nfft <= 0){
        fprintf(stderr,"nfft and ntap both must positive integer\n");
        exit(-1);
    }
    unsigned num = ntap * nfft;
    double * x = malloc(num * sizeof(double));
    double * ham_d = malloc(num * sizeof(double));
    hamming(num, ham_d);

    for(int i = 0;i < num;++i){
        x[i] = 1.0 * i / nfft - 1.0 * ntap / 2;
        coeffs[i] = sinc(x[i]) * ham_d[i];
    }
}


// void load_coeffs(FILE *pf,int ntap,int nfft,double* opt_coeffs)
// {
//     int8_t * coeffs = malloc(ntap*nfft*sizeof(int8_t));
//     int err = fread(coeffs,sizeof(int8_t)*ntap*nfft,1,pf);
//     if(!err){
//         perror("data:load_coeffs");
//         exit(-1);
//     }

//     /* duplicate the coefficients for PFB optimisation */
//     for(int i = 0;i < (ntap*nfft);++i){
//         opt_coeffs[2*i]   = coeffs[i];
//         opt_coeffs[2*i+1] = coeffs[i]; 
//     }
//     free(coeffs);
// }

void load_coeffs(const char *filename,int ntap,int nfft,double *coeffs)
{
    /* if not have coeffs_file,call gen_coeffs function */
    if(filename == NULL){
        gen_coeffs(ntap, nfft, coeffs);
        return;
    }
    FILE *pf = fopen(filename,"r");

    for(int i = 0;i < nfft * ntap;++i){
        fscanf(pf, "%le\n", &coeffs[i]);
    }

    fclose(pf);
}

void opfb_coeffs(double *coeffs,double *ocoeffs,int ntap,int nfft)
{
    for(int i = 0;i < ntap;++i){
        for(int j = 0;j < 2 * nfft;++j){
            if(j < nfft)
                ocoeffs[2*i*nfft+j] = coeffs[i*nfft+j];
            /* To delay the time, make up zeroes in every other column */
            else if(j >= nfft && i < (ntap - 1))
                ocoeffs[2*i*nfft+j] = 0;
        }
    }
}

long long get_file_length(const char *file_name)
{
    struct stat file_stats = {0};
    if(stat(file_name,&file_stats)){
        perror("data:load_data:stat");
        exit(-1);
    }
    return file_stats.st_size;
}

FILE *load_data(const char *filename)
{
    FILE *pf = fopen(filename,"r");
    if(pf == NULL){
        perror("load_data:fopen()");
        exit(1);
    }
    // skip header
    fseek(pf,header_size,SEEK_SET);
    return pf;
}

void opfb_data(double *data,double *odata,double *half_data,int nfft,long block_size)
{
    int half = nfft / 2;
    for(int i = 0;i < half;++i)
        odata[i] = half_data[i];
    for(int i = 0;i < block_size / half;++i){
        for(int j = 0;j < half;++j){
            odata[i*nfft+j+half] = data[i*half+j];
            // Last iteration, no assignment
            if(i < block_size / half-1){
                odata[i*nfft+j+half+half] = data[i*half+j];
            }
        }
    }
    for(int i = 0;i < half;++i)
        half_data[i] = data[block_size - half + i];

    // flip data
    flip_data(odata,nfft,2*block_size);
}

void swap_data(double *d1,double *d2)
{
    double temp;
    temp = *d1;
    *d1 = *d2;
    *d2 = temp;
}

void flip_data(double *odata,int nfft,long size)
{
    for(int i = 0;i < size / nfft;++i){
        for(int j = 0;j < nfft / 2;++j){
            swap_data(&odata[i*nfft+j],&odata[i*nfft+nfft-j-1]);
        }
    }
}

void unpack_dada(FILE *pf,double *pol_x,double *pol_y,long size)
{
    /* dual-polarization */
    char *rawdata = malloc(sizeof(char) * 2 * size);

    if(fread(rawdata,2 * size * sizeof(char),1,pf) != 1){
        perror("unpack_dada:fread()");
        exit(1);
    }
    for(long i = 0;i < size / 4; ++i){
        pol_x[i*4+0] = (double)rawdata[8*i+0];
        pol_x[i*4+1] = (double)rawdata[8*i+1];
        pol_x[i*4+2] = (double)rawdata[8*i+2];
        pol_x[i*4+3] = (double)rawdata[8*i+3];
        pol_y[i*4+0] = (double)rawdata[8*i+4];
        pol_y[i*4+1] = (double)rawdata[8*i+5];
        pol_y[i*4+2] = (double)rawdata[8*i+6];
        pol_y[i*4+3] = (double)rawdata[8*i+7];
    }
    free(rawdata);
}

void write_data(const char *filename,double *data,long size)
{
    FILE *pf = fopen(filename,"w");
    if(pf == NULL){
        perror("write_data:fopen()");
        exit(1);
    }

    for(int i = 0;i < size;++i){
        fprintf(pf,"%f\n",data[i]);
    }

    printf("Write successfully!\n");
    fclose(pf);
}